package unamur.iodass.auction;


import java.util.ArrayList;
import java.util.HashMap;

import org.apache.log4j.Logger;

import unamur.iodass.beans.Client;
import unamur.iodass.catalog.RetreiveAllCatalogs;
import unamur.iodass.communication.Communication;
import unamur.iodass.communication.messages.*;
import unamur.iodass.exception.NoBookStoreAvailableException;
import unamur.iodass.registration.BookStoreHandler;
import unamur.iodass.registration.ParcelDeliveryHandler;

/**
 * Classe permettant de gerer les demandes les commandes et annulations de contrat
 * 
 * @author Cherchem, Marchal
 *
 */
public class AuctionHandler {
	private static final Logger logger = Logger.getLogger(AuctionHandler.class);

	private static volatile AuctionHandler instance;
	private Communication com = Communication.getInstance();
	private BookStoreHandler bookStores = BookStoreHandler.getInstance();

	private HashMap<Long, Boolean> progresses; // liste (isbn, enCoursDeTraitement)

	private HashMap<Long, Client> clients; // liste (isbn, Clients)


	public static AuctionHandler getInstance() {
		if (instance == null) {
			synchronized (AuctionHandler.class) {
				if (instance == null) {
					instance = new AuctionHandler();
				}
			}
		}
		return instance;
	}

	private AuctionHandler() {
		progresses = new HashMap<Long, Boolean>();
		clients = new HashMap<Long, Client>();
	}


	/**
	 * Cette méthode envoie une demande pour obtenir le meilleur prix d'un livre auprès
	 * du BookStore Host qui est choisi aléatoirement entre parmi les BookStore enregistrés auprès
	 * de OnlineLibrary
	 * 
	 * 
	 * @param isbn
	 * 			l'isbn du livre
	 * @param client
	 * 			données du client
	 */
	synchronized public void commandeLivre(long isbn,Client client){

		progresses.put(isbn, true);
		clients.put(isbn, client);

		Book book = RetreiveAllCatalogs.getInstance().getBookByISBN(isbn);

		LibraryRequest libraryRequest = new LibraryRequest("STARTAUCTION", book.getIsbn(), book.getPrice(), bookStores.getBookStores());

		
		/*
		 * 
		 */
		int bsRand = 0;
		try {
			bsRand = randomChoiceOfBS();
		} catch (NoBookStoreAvailableException e1) {
			logger.error("PAS de BookStore disponible --- La demande de prix de livre n'a donc pas été envoyée");
		}
		
		try {
			com.async(libraryRequest, bookStores.getBookStores().get(bsRand).getIp(), bookStores.getBookStores().get(bsRand).getPort());
		} catch (Exception e) {
			
		}


	}

	/**
	 * Cette methode s'occupe de traiter un nouveau contrat venant du BookStore Host
	 * @param cbs 
	 * 			contrat venant du BookStore Host
	 */
	synchronized public void receivedBid(ContratBookStore cbs) {
		logger.info("CONTRAT CONCLU : " + "\t BookStore : " +cbs.getBookStore().getName() + "\t ISBN : " + cbs.getIsbn()  + "\t PRIX : " + cbs.getMinimum());

		progresses.remove(cbs.getIsbn());
		progresses.put(cbs.getIsbn(), false);

		this.notifyAll();

		Client c = clients.get(cbs.getIsbn());

		ClientSerialised clientS = new ClientSerialised(c.getPrenom(), c.getNom(), c.getRue(), c.getNumero(), c.getBoitePostal(),
				c.getCodePostal(), c.getVille(), c.getPays());


		Deliver deliver = new Deliver(cbs.getBookStore(), clientS, cbs.getIsbn());

		if (ParcelDeliveryHandler.getInstance().getDeliveries().size() >= 1) {
		com.async(deliver, 
				ParcelDeliveryHandler.getInstance().getDeliveries().get(0).getIp(),
				ParcelDeliveryHandler.getInstance().getDeliveries().get(0).getPort());
		}
		

	}
	
	/**
	 * Cette methode prend en charge une annulation de contrat venant d'un bookstore
	 *  
	 * @param cancel
	 * 			  Object contenant les informations d'annulation
	 */
	synchronized public void cancelBid(CancelDeliver cancel) {
		logger.info("\n\n");
		logger.info(" ANNULER LA COMMANDE - Note de Crédit");
		logger.info("ISBN : \t\t" + cancel.getIsbn());
		logger.info("CLIENT : \t\t " + cancel.getClient().getNom());
		logger.info("PRICE : \t\t " + RetreiveAllCatalogs.getInstance().getBookByISBN(cancel.getIsbn()).getPrice());
		logger.info("\n\n");
	}

	/**
	 * Permet de récuperer l'object "progresses" sur lequel la servlet est synchronisée
	 * @return
	 */
	synchronized public HashMap<Long, Boolean> getProgresses() {
		return progresses;
	}

	/**
	 * Genere un nombre entier aléatoire de 0 compris à nombre de bookstore non compris
	 * 							[0..nbBookStore-1]
	 * 
	 * @return
	 * @throws NoBookStoreAvailableException
	 */
	synchronized public int randomChoiceOfBS() throws NoBookStoreAvailableException{
		int numberOfBS = bookStores.getBookStores().size();
		
		if (numberOfBS == 0) {
			throw new NoBookStoreAvailableException();
		} 
		
		return (int)(Math.random() * (numberOfBS-1)); // [0..numberOfBS-1]
	}

}
